<?php

namespace app\api\service;

use app\api\service\Token;
use app\api\model\User;
use app\lib\enum\ScopeEnum;
use app\lib\exception\TokenException;
use app\lib\exception\WeChatException;
use Exception;

/**
 * Undocumented class
 */
class UserToken extends Token
{
    private $code;
    private $wxAppID;
    private $wxAppSecret;
    private $wxLoginUrl;

    /**
     * @__construct  拼接LoginUrl（微信用户授权登录api）
     * 
     * @param [type] $code 用户授权登录后随机生成的字符串，用于访问微信api
     */
    public function __construct($code)
    {
        $this->code = $code;
        $this->wxAppID = config('wx.app_id');
        $this->wxAppSecret = config('wx.app_secret');
        $this->wxLoginUrl = sprintf(config('wx.login_url'), $this->wxAppID, $this->wxAppSecret, $this->code);
    }


    /**
     * @get 获得用户令牌
     *
     */
    public function get()
    {
        $result = curl_get($this->wxLoginUrl);
        //json转换为数组格式
        //dump($wxResult);
        //wxResult不存在，则抛出异常
        $wxResult = json_decode($result, true);
        if (empty($wxResult)) {
            throw new Exception('获取到seesion_key及openID异常，微信配置错误');
        } else {
            $loginFail = array_key_exists('errcode', $wxResult);
            //errcode错误码存在，执行processLoginError方法
            if ($loginFail) {
                $this->processLoginError($wxResult);
            }
            //成功获取则执行grantToken方法
            else {
                return $this->grantToken($wxResult);
            }
        }
    }

    //定义grantToken方法，code通过验证则颁发令牌
    private function grantToken($wxResult)
    {
        //取得openID
        //查看数据库是否存在openID  1.若是存在，则返回UId。2.若不存在，则新增一条用户记录,再返回UId
        //准备缓存数据
        //生成令牌，写入缓存
        //把令牌返回到客户端
        //key:令牌
        //value：$wxResult(微信服务器生成的数据：https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html)
        //uid（用户在数据库的唯一标识：绑定该微信的openid）、scope(用户权重：权重越大，权限越高)
        $openid = $wxResult['openid'];
        $uid =  User::getByOpenID($openid);
        $cachedValue = $this->prepareCachedValue($wxResult, $uid);
        $token = $this->saveToCache($cachedValue);
        return $token;
    }

    //定义saveToCache方法，令牌赋值
    private function saveToCache($cachedValue)
    {
        //调用基类静态（self）方法
        //把数组转换为字符串
        //定义缓存过期时间(编写在配置文件extra/setting中)，缓存过期，令牌无效
        //使用TP5提供的方法，把数据写入缓存
        $key = self::generateToken();
        $value = json_encode($cachedValue);
        $expire_in = config('setting.token_expire_in');
        $request = cache($key, $value, $expire_in);
        if (!$request) {
            throw new TokenException([
                'msg' => '服务器缓存异常',
                'errorCode' => 10005
            ]);
        }
        return $key;
    }

    //定义prepareCachedValue方法，拼接$cachedValue缓存数据
    private function prepareCachedValue($wxResult, $uid)
    {
        $cachedValue = $wxResult;
        $cachedValue['uid'] = $uid;
        //权限作用域scope
        $cachedValue['scope'] = ScopeEnum::User;
        return $cachedValue;
    }


    //processLoginError方法返回微信定义的提示
    private function processLoginError($wxResult)
    {
        throw new WeChatException([
            'msg' => $wxResult['errmsg'],
            'errorCode' => $wxResult['errcode']
        ]);
    }
}
